package abc.utils;

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.DecodedJWT;
import org.junit.Assert;

import java.io.IOException;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

public class JwtUtil {

    /**
     * 用户登录成功后生成Jwt
     * 使用Hs256算法  私匙使用用户密码
     *
     * @param ttlMillis jwt过期时间
     * @Param privateKey 密鈅，生成签名的时候使用的秘钥secret,这个方法本地封装了的，一般可以从本地配置文件中读取，切记这个秘钥不能外露哦。它就是你服务端的私钥，在任何场景都不应该流露出去。一旦客户端得知这个secret, 那就意味着客户端是可以自我签发jwt了。
     * @param user      登录成功的user对象
     * @Param account 賬號
     * @return
     */
    public static <T> String genToken(long ttlMillis, String privateKey, T user, String signUserAccount) {
        //生成JWT的时间
        long nowMillis = System.currentTimeMillis();

        //创建payload的私有声明（根据特定的业务需要添加，如果要拿这个做验证，一般是需要和jwt的接收方提前沟通好验证方式的）
        Map<String, Object> claims = new HashMap<String, Object>();
        claims.put("userinfo", JacksonUtil.toJson(user));
        claims.put("sign", signUserAccount);

        if(ttlMillis <= 0) {
            ttlMillis = 720000;
        }
        long expMillis = nowMillis + ttlMillis;
        Date exp = new Date(expMillis);
        String token = JWT.create()
                .withExpiresAt(exp)  //设置过期时间
                .withAudience(signUserAccount) //设置接受方信息，一般为登录账号
                .withSubject(JacksonUtil.toJson(claims))
                .sign(Algorithm.HMAC256(privateKey));  //使用HMAC算法，privateKey作为密钥加密

        return token;
    }


    /**
     * Token的解密
     * @param token 加密后的token
     * @return
     */
    private static Map getSubjectMap(String token, String privateKey) throws IOException {
        DecodedJWT decoded = JWT.decode(token);
        JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(privateKey)).build();
        decoded = jwtVerifier.verify(token);
        Map data = JacksonUtil.parse(decoded.getSubject(), Map.class);
        String audienceString = decoded.getAudience().get(0);
        Assert.assertEquals(data.get("sign"), audienceString);
        data.put("sign", audienceString.equals(data.get("sign")));
        return data;
    }

    /**
     * Token的解密
     * @param token 加密后的token
     * @return 返回实体对象
     */
    public static <T> T parse(String token, String privateKey, Class<T> userClass) throws IOException {
        Map data = getSubjectMap(token, privateKey);
        T obj = JacksonUtil.parse(data.get("userinfo").toString(), userClass);
        return obj;
    }

    /**
     * 校验token
     * @param token
     * @return
     */
    public static <T> Boolean isVerify(String token, String privateKey) {
        try {
            Map data = getSubjectMap(token, privateKey);
            if (data.get("sign") == null) {
                return false;
            }
            if (((Boolean) data.get("sign")) == true) {
                return true;
            }
            return false;
        } catch (Exception e) {
            return false;
        }
    }
}
